/***************************************************************
*
* DS_AFFIXES.CPP
*
* affix string
***************************************************************/
#include <stdarg.h>
#include "ap\ap_comm.h"
#include "ds_affixes.h"
#include "ds_comm.h"
#include "ds_vector.h"

#include "ps_sys.h"

dsVECTOR<dsPAIR<dsSTRING, dsSTRING> > dsAFFIX_STRING::affixAliases(AP_CL);

/*************************************************************
* dsAFFIX_STRING::HaveAffix()
*
*************************************************************/
bool dsAFFIX_STRING::HaveAffix(const char *aff) const
{
   return Find(aff) != -1;
}

/**************************************************************************
* dsAFFIX_STRING::ToLogString()                                           *
*                                                                         *
**************************************************************************/
dsSTRING dsAFFIX_STRING::ToLogStr(void) const
{
   dsSTRING res;
   res.Reserve(128);

   dsAFFIX_BUILDER builder(*this);
   for (int i=0; i<builder.GetAffixCount(); ++i) {
      if (i > 0) {
         res += ",";
      }
      res += builder.GetAffixName(i);
      
      for (int k=0; k<builder.GetNParams(i); ++k) {
         res += dsSPrintf("|%s=%s", builder.GetParamName(i, k).CStr(), builder.GetParamValue(i, k).CStr());
      }
   }
   
   return res;
}

/*************************************************************
* dsAFFIX_STRING::Normalize()
*
*************************************************************/
//void dsAFFIX_STRING::Normalize(void)
//{
//   if (IsEmpty()) {
//      return;
//   }
//   
//   dsVECTOR<dsSTRING> affArr(AP_CL);
//
//   int pos = Find('&');
//   if (pos == -1) {
//      *(dsSTRING*)this = "";
//      return;
//   }
//
//   while (pos < Length()) {
//      int end = Find('&', pos + 1);
//    
//      if (end == -1) {
//         end = Length();
//      }
//
//      dsSTRING aff = SubStr(pos, end - pos);
//      pos = end;
//
//      while (aff[aff.Length() - 1] == '_') {
//         aff.Erase(aff.Length() - 1, 1);
//      }
//      if (aff.Length() < 2) {
//         continue;
//      }
//
//      affArr.InsertSorted(aff, dsCMP());
//   }
//
//   Clear();
//   for (int i = 0; i < affArr.Length(); i++) {
//      *this += affArr[i];
//   }
//   Compact();
//
//   return;
//}
//
//

////
//// class dsAFFIX_READER
////
///*************************************************************
//* dsAFFIX_READER::dsAFFIX_READER()
//*
//*************************************************************/
//dsAFFIX_READER::dsAFFIX_READER(const dsSTRING &_str) : str()
//{
//   str         = _str;
//   curAffixOfs = -1;
//   numParams   = -1;
//   return;
//}
//
///*************************************************************
//* dsAFFIX_READER::Find()
//*
//*************************************************************/
//BOOL dsAFFIX_READER::Find(const char *aff)
//{
//   curAffixOfs = str.Find(aff);
//   numParams   = -1;
//   return curAffixOfs != -1;
//}
//
///*************************************************************
//* dsAFFIX_READER::ResetFind()
//*
//*************************************************************/
//void dsAFFIX_READER::ResetFind(void)
//{
//   curAffixOfs = -1;
//   return;
//}
//
///*************************************************************
//* dsAFFIX_READER::FindNext()
//*
//*************************************************************/
//BOOL dsAFFIX_READER::FindNext(void)
//{
//   curAffixOfs = str.Find('&', curAffixOfs + 1);
//   return curAffixOfs != -1;
//}
//
///*************************************************************
//* dsAFFIX_READER::GetStr()
//*
//*************************************************************/
//BOOL dsAFFIX_READER::GetStr (int idx, dsSTRING &val)
//{
//   ScanParams();
//   if (idx < 0 || idx >= numParams) {
//      return FALSE;
//   }
//   val = (str.CStr() + affixParamOfs[idx]);
//   int i = val.Find('&');
//   if (i != -1) {
//      val.Erase(i, -1);
//   }
//   i = val.Find('_');
//   if (i != -1) {
//      val.Erase(i, -1);
//   }
//   return TRUE;
//}
///*************************************************************
//* dsAFFIX_READER::GetNParams()
//*
//*************************************************************/
//int dsAFFIX_READER::GetNParams(void)
//{
//   return ScanParams();
//}
//
///*************************************************************
//* dsAFFIX_READER::GetFloat()
//*
//*************************************************************/
//BOOL dsAFFIX_READER::GetFloat(int idx, float *val)
//{
//   ScanParams();
//   if (idx < 0 || idx >= numParams) {
//      return FALSE;
//   }
//   *val = (float)atof(str.CStr() + affixParamOfs[idx]);
//   return TRUE;
//}
//
///*************************************************************
//* dsAFFIX_READER::GetInt()
//*
//*************************************************************/
//BOOL dsAFFIX_READER::GetInt(int idx, int *val)
//{
//   ScanParams();
//   if (idx < 0 || idx >= numParams) {
//      return FALSE;
//   }
//   *val = atoi(str.CStr() + affixParamOfs[idx]);
//   return TRUE;
//}
//
///*************************************************************
//* dsAFFIX_READER::GetHex()
//*
//*************************************************************/
//BOOL dsAFFIX_READER::GetHex(int idx, DWORD *val)
//{
//   ScanParams();
//   if (idx < 0 || idx >= numParams) {
//      return FALSE;
//   }
//   return sscanf(str.CStr() + affixParamOfs[idx], "%lu", val) == 1;
//}
//
///*************************************************************
//* dsAFFIX_READER::GetFloat()
//*
//*************************************************************/
//float dsAFFIX_READER::GetFloat(int idx, float def)
//{
//   GetFloat(idx, &def);
//   return def;
//}
//
///*************************************************************
//* dsAFFIX_READER::GetInt()
//*
//*************************************************************/
//int dsAFFIX_READER::GetInt(int idx, int def)
//{
//   GetInt(idx, &def);
//   return def;
//}
//
///*************************************************************
//* dsAFFIX_READER::GetHex()
//*
//*************************************************************/
//DWORD dsAFFIX_READER::GetHex(int idx, DWORD def)
//{
//   GetHex(idx, &def);
//   return def;
//}
//
///*************************************************************
//* dsAFFIX_READER::GetParams()
//*
//*************************************************************/
//dsSTRING dsAFFIX_READER::GetParams(void)
//{
//   if (curAffixOfs == -1) {
//      return "";
//   }
//   
//   int ofs = curAffixOfs + 1;
//
//   while (str[ofs] != 0 && str[ofs] != '&' && str[ofs] != '_') {
//      ofs++;
//   }
//
//   if (str[ofs] != '_') {
//      return "";
//   }
//
//   int begin = ofs + 1;
//   ofs = begin;
//   while (str[ofs] != 0 && str[ofs] != '&') {
//      ofs++;
//   }
//
//   return str.SubStr(begin, ofs - begin);
//}
//
///*************************************************************
//* dsAFFIX_READER::ScanParams()
//*
//*************************************************************/
//int dsAFFIX_READER::ScanParams(void)
//{
//   if (numParams != -1) {
//      // already scaned
//      return numParams;
//   }
//   
//   if (curAffixOfs == -1) {
//      return 0;
//   }
//
//   numParams = 0;
//   int ofs = curAffixOfs + 1;
//
//   for (numParams = 0; numParams < _NElem(affixParamOfs); numParams++) {
//      // look for '_'
//      while (str[ofs] != 0 && str[ofs] != '&' && str[ofs] != '_') {
//         ofs++;
//      }
//      if (str[ofs] != '_') {
//         break;
//      }
//      while (str[ofs] == '_') {
//         ofs++;
//      }
//      if (str[ofs] == 0 || str[ofs] == '&') {
//         break;
//      }
//      
//      affixParamOfs[numParams] = ofs;
//   }
//
//   return numParams;
//}

/**********************************************************************
* _scnDumpInstList()
*
**********************************************************************/
void dsAFFIX_STRING::InsertAliases(void)
{
   int prevPos = 0;

   if (affixAliases.IsEmpty()) {
      return;
   }
   
   int alias = Find("&x");
   if (alias == -1) {
      return;
   }

   dsSTRING newStr;
   newStr.Reserve(Length() * 2);

   dsSTRING aff;

   BOOL changeMade = FALSE;
   do {
      newStr.Insert(newStr.Length(), CStr() + prevPos, alias - prevPos);
      prevPos = alias;

      int next = Find('&', alias + 2);
      if (next == -1) {
         next = Length();
      }

      aff.Clear();
      aff.Insert(0, CStr() + alias + 1, next - (alias + 1));

      int a = affixAliases.FindSorted(aff);
      if (a != -1) {
         newStr += affixAliases[a].val;
         changeMade = TRUE;
         prevPos = next;
      } else {
         if (affixAliases.Length() > 0 && aff[0] >= 'A' && aff[0] <= 'Z') {
            apLog("~Error,Scene~Undefined affix alias '%s'", aff.CStr());
         }
      }

      alias = Find("&x", alias + 2);
   } while (alias != -1);

   newStr.Insert(newStr.Length(), CStr() + prevPos, Length() - prevPos);

   if (changeMade) {
      *this = newStr;
      //Normalize();
      Compact();
   }
   return;
}

/**********************************************************************
* dsAFFIX_STRING::ReadAffixAliases()
*
**********************************************************************/
void dsAFFIX_STRING::ReadAffixAliases(const psSECTION &psRead)
{
   affixAliases.Clear();
   for (psITERATOR it(psRead->GetSection("aliases"), PS_TYPEID_SECTION); !it.IsDone(); it.Next()) {
      psSECTION ps = it.GetSection();
      dsSTRING name = it.GetKeyName().CStr();
      dsSTRING val  = ps->GetStr("value");
      affixAliases.InsertSorted(dsMakePair(name, val));
   }
   return;
}

/**********************************************************************
* dsAFFIX_STRING::ReadAffixAliases()
*
**********************************************************************/
void dsAFFIX_STRING::ReadAffixAliases(const psARRAY &arr)
{
   affixAliases.Clear();
   for (int i = 0; i < arr.Length(); i++) {
      dsSTRING id = arr.GetSection(i)->GetStr("id");
      dsSTRING val = arr.GetSection(i)->GetStr("value");
      if (id != "" && val != "") {
         affixAliases.InsertSorted(dsMakePair(id, val));
      }

   }
   return;
}


//////////////////////////////////////////////////////////////////////////

/************************************
* dsAFFIX_READER2::dsAFFIX_READER2
*
************************************/
dsAFFIX_READER::dsAFFIX_READER (const dsSTRING& affixes) 
   : affixStr(affixes), nSubStr(0), curSubStr(-1)
{
   Init();
}


/************************************
* dsAFFIX_READER2::HaveAffix
*
************************************/
bool dsAFFIX_READER::HaveAffix (const char* affix, BOOL asSubString)
{
   curSubStr = FindAffix(affix, asSubString);
   return curSubStr != -1;
}


/************************************
* dsAFFIX_READER2::GetFloat
*
************************************/
bool dsAFFIX_READER::GetFloat (const char* paramName, float* val) const
{
   int param = GetParam(paramName);
   if (param < 0) {
      return false;
   }

   *val = (float)atof(affSubStr[param].ptr);
   return true;
}


/************************************
* dsAFFIX_READER2::GetFloat
*
************************************/
float dsAFFIX_READER::GetFloat (const char* paramName, float defVal) const
{
   float val;
   return GetFloat(paramName, &val) ? val : defVal;
}


/************************************
* dsAFFIX_READER::GetFloat
*
************************************/
bool dsAFFIX_READER::GetFloat (int n, float* val) const
{
   int param = GetParam(n);
   if (param < 0) {
      return false;
   }

   *val = (float)atof(affSubStr[param].ptr);
   return true;
}

/************************************
* dsAFFIX_READER::GetInt
*
************************************/
bool dsAFFIX_READER::GetInt(int n, int* val) const
{
   int param = GetParam(n);
   if (param < 0) {
      return false;
   }

   *val = (int)atoi(affSubStr[param].ptr);
   return true;
}


/************************************
* dsAFFIX_READER2::GetInt
*
************************************/
bool dsAFFIX_READER::GetInt (const char* paramName, int* val) const
{
   int param = GetParam(paramName);
   if (param < 0) {
      return false;
   }

   *val = atoi(affSubStr[param].ptr);
   return true;
}


/************************************
* dsAFFIX_READER2::GetInt
*
************************************/
int dsAFFIX_READER::GetInt (const char* paramName, int defVal) const
{
   int val;
   return GetInt(paramName, &val) ? val : defVal;
}


/************************************
* dsAFFIX_READER2::GetString
*
************************************/ 
bool dsAFFIX_READER::GetString (const char* paramName, dsSTRING* val) const
{
   int param = GetParam(paramName);
   if (param < 0) {
      return false;
   }

   char* dstPtr = val->Lock(affSubStr[param].length);
   memcpy(dstPtr, affSubStr[param].ptr, affSubStr[param].length);
   val->Unlock();
   return true;
}


/************************************
* dsAFFIX_READER2::Init
*
************************************/
void dsAFFIX_READER::Init(void)
{
   int pos = 0;
   int type = AFFIX;
   while (true) {
      int newPos = affixStr.Find('\n', pos);
      if (newPos < 0) {
         break;
      }

      if (newPos - pos > 0) {
         affSubStr[nSubStr].ptr = affixStr.CStr() + pos;
         affSubStr[nSubStr].type = type;
         affSubStr[nSubStr].length = newPos - pos;
         type = (type == AFFIX || type == PARAM_VALUE) ? PARAM_NAME : PARAM_VALUE;
         nSubStr++;
         STRONG_ASSERT(nSubStr <= subStrCount);
      }else {
         type = AFFIX;
      }
      pos = newPos + 1;
   }
}


/************************************
* dsAFFIX_READER2::Find
*
************************************/
int dsAFFIX_READER::FindAffix (const char* affix, BOOL asSubString) const
{
   int affixLen = strlen(affix);
   for (int i = 0; i < nSubStr; ++i) {
      if (affSubStr[i].type != AFFIX) {
         continue;
      }
      if (!asSubString) {
         if (affSubStr[i].length != affixLen) {
            continue;
         }
      }
      if (memcmp(affix, affSubStr[i].ptr, affixLen) == 0) {
         return i;
      }
   }
   return -1;
}


/************************************
* dsAFFIX_READER2::GetParam
*
************************************/
int dsAFFIX_READER::GetParam (const char* paramName) const
{
   if (curSubStr < 0) {
      return -1;
   }

   int pnLen = strlen(paramName);
   int p = curSubStr + 1;
   while(p < nSubStr && affSubStr[p].type == PARAM_NAME) {
      if (affSubStr[p].length == pnLen && memcmp(affSubStr[p].ptr, paramName, pnLen) == 0) {
         return p + 1;
      }
      p += 2;
   }

   return -1;
}


/************************************
* dsAFFIX_READER::GetParam
*
************************************/
int dsAFFIX_READER::GetParam (int paramInd) const
{
   if (curSubStr < 0 || curSubStr + paramInd * 2 + 2 >= nSubStr) {
      return -1;
   }
   
   return curSubStr + paramInd * 2 + 2;
}

/************************************
* dsAFFIX_READER2::HaveAffixParam
*
************************************/
bool dsAFFIX_READER::HaveAffixParam (const char* affix, const char* param)
{
   if (!HaveAffix(affix)) {
      return false;
   }

   return GetParam(param) >= 0;
}


/************************************
* dsAFFIX_READER2::GetBool
*
************************************/
bool dsAFFIX_READER::GetBool (const char* paramName, bool* val) const
{
   int v = 0;
   if (!GetInt(paramName, &v)) {
      return false;
   }
   *val = v != 0;
   return true;
}


/************************************
* dsAFFIX_READER2::GetBool
*
************************************/
bool dsAFFIX_READER::GetBool (const char* paramName, bool defVal) const
{
   bool val = false;
   return GetBool(paramName, &val) ? val : defVal;
}


/************************************
* dsAFFIX_READER2::IsParamEqual
*
************************************/
bool dsAFFIX_READER::IsParamEqual (const char* paramName, const char* value) const
{
   int param = GetParam(paramName);
   if (param < 0) {
      return false;
   }

   int pnLen = strlen(value);
   if (affSubStr[param].length == pnLen && memcmp(affSubStr[param].ptr, value, pnLen) == 0) {
      return true;
   }
   return false;
}


/************************************
* dsAFFIX_READER2::GetNParams
*
************************************/
int dsAFFIX_READER::GetNParams (void) const
{
   if (curSubStr < 0) {
      return -1;
   }

   int count = 0;
   for (int i = curSubStr + 1; i < nSubStr; i += 2) {
      if (affSubStr[i].type == AFFIX) {
         break;
      }
   }

   return count;
}


/************************************
* dsAFFIX_READER::GetStr
*
************************************/
bool dsAFFIX_READER::GetStr (int n, dsSTRING& s) const
{
   int param = GetParam(n);
   if (param < 0) {
      return false;
   }

   char* dstPtr = s.Lock(affSubStr[param].length);
   memcpy(dstPtr, affSubStr[param].ptr, affSubStr[param].length);
   s.Unlock();

   return true;
}


/************************************
* dsAFFIX_READER::FindNext
*
************************************/
bool dsAFFIX_READER::NextAffix (void)
{
   int pos = curSubStr < 0 ? 0 : curSubStr + 1;
   while (pos < nSubStr) {
      if (affSubStr[pos].type == AFFIX) {
         curSubStr = pos;
         return true;
      }
      pos++;
   }
   
   curSubStr = -1;
   return false;
}

/**************************************************************************
* * dsAFFIX_READER::GetCurAffix()                                         *
*                                                                         *
**************************************************************************/
const char * dsAFFIX_READER::GetCurAffix(void) const
{
   STRONG_ASSERT(curSubStr >= 0 && curSubStr < nSubStr);
   STRONG_ASSERT(affSubStr[curSubStr].type == AFFIX);
   return affSubStr[curSubStr].ptr;
}

//////////////////////////////////////////////////////////////////////////

dsAFFIX_BUILDER::dsAFFIX_BUILDER (const dsSTRING& affixString) : affixes(AP_CL)
{
   enum e { AFFIX = 0, PARAM_NAME, PARAM_VALUE };
   int pos = 0;
   int type = AFFIX;

   dsSTRING str[3];

   while (true) {
      int newPos = affixString.Find('\n', pos);
      if (newPos < 0) {
         break;
      }

      if (newPos - pos > 0) {
         char* dstPtr = str[type].Lock(newPos - pos);
         memcpy(dstPtr, affixString.CStr() + pos, newPos - pos);
         str[type].Unlock();
         
         if (type == PARAM_VALUE || type == AFFIX) {
            int index = Add(str[AFFIX]);
            Add(index, str[PARAM_NAME], str[PARAM_VALUE]);
         }

         type = (type == AFFIX || type == PARAM_VALUE) ? PARAM_NAME : PARAM_VALUE;
      }else {
         type = AFFIX;
         str[AFFIX].Clear();
         str[PARAM_VALUE].Clear();
         str[PARAM_NAME].Clear();
      }
      pos = newPos + 1;
   }
}


/************************************
* dsAFFIX_BUILDER::FindAffix
*
************************************/
int dsAFFIX_BUILDER::FindAffix (const dsSTRING& name) const
{
   for (int i = 0; i < affixes.Length(); ++i) {
      if (affixes[i].name == name) {
         return i;
      }
   }
   return -1;
}

/**************************************************************************
* dsAFFIX_BUILDER::Add()                                                  *
*                                                                         *
**************************************************************************/
int dsAFFIX_BUILDER::Add(const dsSTRING & name)
{
   const int index = FindAffix(name);
   if (index != -1) {
      return index;
   }

   affixes.PushBack(AFFIX());
   affixes.Back().name = name;
   return affixes.Length() - 1;
}

/*************************************************************************/
/* dsAFFIX_BUILDER::Add 
/*
/*************************************************************************/
void dsAFFIX_BUILDER::Add(int affixIndex, const dsSTRING& paramName, int paramVal)
{
   Add(affixIndex, paramName, dsSPrintf("%d", paramVal));
}

/*************************************************************************/
/* dsAFFIX_BUILDER::Add 
/*
/*************************************************************************/
void dsAFFIX_BUILDER::Add(int affixIndex, const dsSTRING& paramName, float paramVal)
{
   Add(affixIndex, paramName, dsSPrintf("%f", paramVal));
}

/************************************
* dsAFFIX_BUILDER::Add
*
************************************/
void dsAFFIX_BUILDER::Add (int affixIndex, const dsSTRING& paramName, const dsSTRING& paramVal)
{
   AFFIX * af = &affixes[affixIndex];

   if (paramName.IsEmpty() || paramVal.IsEmpty()) {
      return;
   }

   for (int i = 0; i < af->params.Length(); ++i) {
      if (af->params[i].name == paramName) {
         af->params[i].value = paramVal;
         return;
      }
   }

   AFF_PARAM ap;
   ap.name = paramName;
   ap.value = paramVal;
   af->params.PushBack(ap);
}


/************************************
* dsAFFIX_BUILDER::IsHierarchical
*
************************************/
bool dsAFFIX_BUILDER::IsHierarchical (int idAffix) const
{
   for (int i = 0; i < affixes[idAffix].params.Length(); ++i) {
      if (affixes[idAffix].params[i].name == "is_hierarchical") {
         int v = atoi(affixes[idAffix].params[i].value.CStr());
         return v != 0;
      }
   }

   return false;
}


/************************************
* dsAFFIX_BUILDER::CopyFrom
*
************************************/
void dsAFFIX_BUILDER::AddAffixFrom (const dsAFFIX_BUILDER& ab, int idAffix)
{
   affixes.PushBack(ab.affixes[idAffix]);
}


/************************************
* dsAFFIX_BUILDER::BuildString
*
************************************/
struct SRT_CMP
{
   int cmp(const dsAFFIX_BUILDER::AFFIX& a, const dsAFFIX_BUILDER::AFFIX& b) const {
      return strcmp(a.name.CStr(), b.name.CStr());
   }
};


/**************************************************************************
* dsAFFIX_BUILDER::AppendAffix()                                          *
*                                                                         *
**************************************************************************/
void dsAFFIX_BUILDER::AppendAffix(dsSTRING & str, const AFFIX & p)
{
   str += p.name;
   str += "\n";
   for (int j = 0; j < p.params.Length(); ++j) {
      str += p.params[j].name;
      str += "\n";
      str += p.params[j].value;
      str += "\n";
   }
   str += "\n";
}

/**************************************************************************
* dsAFFIX_BUILDER::BuildString()                                          *
*                                                                         *
**************************************************************************/
dsSTRING dsAFFIX_BUILDER::BuildString (void) const
{
   dsVECTOR<AFFIX> affixes = this->affixes;

   affixes.Sort(SRT_CMP());

   dsSTRING str;
   for (int i=0; i<affixes.Length(); ++i) {
      AppendAffix(str, affixes[i]);
   }
   
   return str;
}

/**************************************************************************
* dsAFFIX_BUILDER::BuildString()                                          *
*                                                                         *
**************************************************************************/
dsSTRING dsAFFIX_BUILDER::BuildString(int affixIndex) const
{
   dsSTRING str;
   AppendAffix(str, affixes[affixIndex]);
   return str;
}

/************************************
* dsAFFIX_BUILDER::GetParamValue
*
************************************/
const dsSTRING dsAFFIX_BUILDER::GetParamValue(int idAff, const dsSTRING pname) const
{
   for (int p = 0; p < affixes[idAff].params.Length(); ++p) {
      if (affixes[idAff].params[p].name == pname) {
         return affixes[idAff].params[p].value;
      }
   }

   return dsSTRING();
}

/************************************
* dsAFFIX_BUILDER::AddAllFrom
*
************************************/
void dsAFFIX_BUILDER::AddAllFrom(const dsAFFIX_BUILDER& ab, bool skip_exist /* = true */)
{
   for (int i = 0; i < ab.GetAffixCount(); ++i) {
      if (!skip_exist || FindAffix(ab.GetAffixName(i)) == -1) {
         AddAffixFrom(ab, i);
      }
   }

   return;
}
